#pragma once
#include"compiler.hpp"
#include"oj_views.hpp"
#include"oj_model.hpp"
#include"Util.hpp"
#include"./cpp-httplib-master/httplib.h"
#include"./CSmtp-master/Smtp.h"
#include<jsoncpp/json/json.h>
#include<vector>
#include<mutex>
#include<thread>

//class HttpServer;/*前置声明不能使用方法,要使用方法要将函数声明定义分离。并且前置声明只能表明类指针或引用*/

namespace Y{
  /*单例模式*/
  using namespace httplib;
  class Oj_Control{
      private:
        /*验证Cookie,点击其他页面和cookie失效后提交要验证*/
        bool StillHasCookie(const httplib::Request& req, httplib::Response& res) {
              //LOG(INFO, req.body);
              /*解析headers的cookie*/
              if (req.has_header("Cookie")) {
                 LOG(INFO, req.get_header_value("Cookie"));
              }
              //Json::reader 读取Json格式信息,api:read.parse(std::string, Json::Value obj);
              Json::Value cookieObj;
              if (req.has_header("Cookie")) {
                  UrlUtil::CookieParse(req.get_header_value("Cookie"), cookieObj);
                  LOG(INFO, "Cookie parse down");
                  std::string username = cookieObj["username"].asString();
                  std::string password = cookieObj["password"].asString();
                  LOG(INFO, "Cookie username:" + username);
                  LOG(INFO, "Cookie password:" + password);
              }
              //Json::StyledWriter stylewriter;//json格式写入器
              /*查看数据库注册用户表的缓存*/
              if (_ojModelObj.UserLogin(cookieObj["username"].asString(), cookieObj["password"].asString())) {
                  return true;
              } 
              else {
                  return false;
              }
        }  
      public:
        Oj_Control(const Oj_Control& ths) = delete;
        Oj_Control& operator=(const Oj_Control& ths) = delete;
        static Oj_Control* GetInstance() {
          if (_ojControl == nullptr){
            Oj_Control::_mtx.lock();
            if(_ojControl == nullptr) {
                _ojControl = new Oj_Control();
                _ojModelObj.Load();
            }
            Oj_Control::_mtx.unlock();
          } 
          return Oj_Control::_ojControl;
        }
        /*设置默认的web根目录和监听端口*/
        bool SetConfigAndRun() {
          if (!_server.set_mount_point("/", "./Web")) {
             LOG(INFO, "绑定失败");
             return false; 
          }
          LOG(INFO, "绑定成功");
          _server.listen("0.0.0.0", 8080);
          // _server.Loop();
          return true;
        }
        /*回调题库*/
        bool SetProblemPage() {
             LOG(INFO, "SetProblemPage 回调");
             /*Test*/
             // _server.Get("/hi", [](const httplib::Request &, httplib::Response &res) {
             //     res.set_content("Hello World!", "text/plain");
             // });
             _server.Get("/problem.html", [&](const httplib::Request& req, httplib::Response& res) {
                 //res.set_content("Hello World!", "text/plain");
                 LOG(INFO, "SetProblemPage 回调");
                 (void) req;
                 std::vector<Json::Value> _kvObj; 
                 std::string html;
                 _ojModelObj.GetAllQuestions(_kvObj);
                 Oj_View::RenderAllQuestions(_kvObj, html);
                 res.status = 200; /*状态码一定要手动设定*/
                 res.set_content(html, "text/html");/*set_content一定要写,设置动态属性的,返回处理好的内容和对应文件格式的后缀名*/
             }); 
             return true;
        }
        /*捕捉查看代码*/
        bool SetLookCode() {
             _server.Get(R"(/tempFiles/(\d+).cc)", [&](const httplib::Request& req, httplib::Response& res) {
                 LOG(INFO, "SetLookCode 回调");
                 (void) req;
                 std::string context;
                 Json::Value _kvObj;
                 /*代码文件在上级目录*/
                 FileUtil::ReadFile("." + req.path, context);/*读取请求代码文件,前端会吞'.'*/
                 _kvObj["code"] = context;
                 std::string html;
                 Oj_View::RenderCode(_kvObj, html); 
                 res.status = 200; /*状态码一定要手动设定*/
                 res.set_content(html, "text/html");/*set_content一定要写,设置动态属性的,返回处理好的内容和对应文件格式的后缀名*/
             }); 
             return true;
        }
        /*设置提交题目代码的回调机制*/
        bool SetProblemSubmit() {
              _server.Get(R"(/submitPage/(\d+))", [&](const httplib::Request& req, httplib::Response& res){
                  (void) req; 
                  LOG(INFO, req.path);
                  std::string html;
                  Json::Value _Obj; 
                  if (_ojModelObj.GetOneQuestion(req.matches[1].str(), _Obj)) {
                      LOG(INFO, "GetOneQuestion is ok");
                      Oj_View::RenderOneQuestion(_Obj, html);
                      res.status = 200;
                      res.set_content(html, "text/html");
                  }
                  else {
                      LOG(INFO, "GetOneQuestion is error");
                  }
              });
              return true;
        }
        /*返回调试代码运行结果的页面*/
        bool SetCompileDebugPage() {
             return true;
        }
        /*返回提交代码运行结果的页面*/
        bool SetCompileResultPage() {
          _server.Post(R"(/compile/(\d+))", [&](const httplib::Request& req, httplib::Response& res){
                 LOG(INFO, req.body);
                 if (req.has_header("Cookie")) {
                    LOG(INFO, req.get_header_value("Cookie"));
                 }
                 { /*后端拦截未登录用户,针对无ajax验证*/
                    if (StillHasCookie(req, res)) {
                       //.. do nothing 
                    } 
                    else {
                        std::string html; 
                        FileUtil::ReadFile("./Web/login.html", html);
                        res.set_content(html, "text/html");
                        return ;
                    }
                 }
                 /*将输入转成Json格式*/
                 std::unordered_map<std::string, std::string> kv_map;/*将req进行切分*/
                 UrlUtil::UrlParse(req.body, kv_map); 
                 Json::Value js_req; 
                 if (_ojModelObj.GetOneQuestion(req.matches[1].str(), js_req)) {
                     LOG(INFO, "GetOneQuestion is ok");
                 }
                 /*Code*/
                 for (auto& i: kv_map) {
                     js_req[i.first] = i.second; 
                     LOG(INFO, i.first + ":" + i.second);
                 } 
                 /*进行编译,构建json格式响应*/
                 /*保证优先出提交结果,再更新记录*/
                 Json::Value js_res;
                 Compile::CompileAndRun(js_req, js_res); 
                 std::string html;
                 Oj_View::RenderSubmitStatus(js_res, html);
                    /*解析Cookie*/
                 Json::Value cookieObj;
                 if (req.has_header("Cookie")) {
                     UrlUtil::CookieParse(req.get_header_value("Cookie"), cookieObj);
                 } 
                 LOG(INFO, cookieObj.toStyledString());
                    /*构建插入sql的元素*/
                 Json::Value insertV; 
                 insertV["User"] = cookieObj["username"]; /*要在cookie里面拿了*/
                 insertV["ProblemID"] = js_req["ProblemID"];
                 insertV["FilePath"] = js_res["filepath"];
                 insertV["Status"] = js_res["status"];
                 res.set_content(html, "text/html");
                 /*增加缓存中的提交总数*/
                 _ojModelObj.AddHistorySize();
                 /*往History表中插入记录*/
                 _ojModelObj.HistoryInsert(insertV);
          });
        } 
        /*跳转页面时通过cookie判断用户是否已经登录,针对ajax验证*/
        bool CheckUserIfLogin() {
              /*先用body传参*/
              _server.Post("/CheckUserIfLogin", [&](const httplib::Request& req, httplib::Response& res) {
                  if (StillHasCookie(req, res)) {
                      res.status = 200; 
                      res.set_content("true", "text/plain");
                  }            
                  else {
                      res.status = 200; 
                      res.set_content("false", "text/plain");
                  }
              });
        }
        /*用户登录,理论上是后端set_cookie返回*/
        bool SetUserLogin() {
              _server.Post("/accountCheck", [&](const httplib::Request& req, httplib::Response& res) {
                    LOG(INFO, req.body);
                    /*将输入转成Json格式*/
                    std::unordered_map<std::string, std::string> kv_map;/*将req进行切分*/
                    UrlUtil::UrlParse(req.body, kv_map); 
                    LOG(INFO, kv_map["username"]);
                    LOG(INFO, kv_map["password"]);
                    if (_ojModelObj.UserLogin(kv_map["username"], kv_map["password"])) {
                        LOG(INFO, kv_map["username"] + "logins successfully");
                        /**/
                        std::string html;
                        FileUtil::ReadFile("./Web/index.html", html); 
                        //Oj_View::RenderAllQuestions(_kvObj, html);
                        res.status = 200; /*状态码一定要手动设定*/
                        res.set_content(html, "text/html");/*set_content一定要写,设置动态属性的,返回处理好的内容和对应文件格式的后缀名*/
                    }
                    else {
                        LOG(INFO, kv_map["username"] + "logins unsuccessfully " + kv_map["password"]);
                        std::string html;
                        FileUtil::ReadFile("./Web/login.html", html); 
                        res.status = 200; /*状态码一定要手动设定*/
                        res.set_content(html, "text/html");/*set_content一定要写,设置动态属性的,返回处理好的内容和对应文件格式的后缀名*/
                    }

              });
        }
        /*用户注册*/
        bool SetUserRegister() {
              _server.Post("/registeraccount", [&](const httplib::Request& req, httplib::Response& res) {
                    LOG(INFO, req.body);
                    /*将输入转成Json格式*/
                    std::unordered_map<std::string, std::string> kv_map;/*将req进行切分*/
                    UrlUtil::UrlParse(req.body, kv_map); 
                    srand((unsigned)time(nullptr));
                    int num = 8; 
                    std::string password;
                    for (int i = 1; i <= 8; i++) {
                        password += '0' + rand()%10;
                    }
                    /*插入用户数据*/
                    if (_ojModelObj.AccountInsert(kv_map["email"], password)) {
                        LOG(INFO, "注册成功,发送邮箱");
                        if (OjSendEmail(kv_map["email"], password) == 0) {
                            LOG(ERROR, "email send error");
                            return false;
                        }
                        std::string html;
                        FileUtil::ReadFile("./Web/register.html", html); 
                        res.status = 200; /*状态码一定要手动设定*/
                        res.set_content(html, "text/html");/*set_content一定要写,设置动态属性的,返回处理好的内容和对应文件格式的后缀名*/
                    }
                    else {
                        LOG(INFO, "该邮箱已经注册过");
                        res.status = 200; /*状态码一定要手动设定*/
                        std::string message ="The account has registered";
                        res.set_content(message, "text/plain");/*set_content一定要写,设置动态属性的,返回处理好的内容和对应文件格式的后缀名*/
                        return false;
                    }
              });
              return true;
        }
        /*显示用户的提交记录*/
        bool SetUserHistoryStatus() {
              _server.Post(R"(/submitLogD)", [&](const httplib::Request& req, httplib::Response& res) {
                        if (req.has_header("Cookie")) {
                           LOG(INFO, req.get_header_value("Cookie"));
                        }
                        //Json::reader 读取Json格式信息,api:read.parse(std::string, Json::Value obj);
                        Json::Value cookieObj;
                        std::string username = "";
                        if (req.has_header("Cookie")) {
                            UrlUtil::CookieParse(req.get_header_value("Cookie"), cookieObj);
                            LOG(INFO, "Cookie parse down");
                             username = cookieObj["username"].asString();
                            LOG(INFO, "Cookie username:" + username);
                        }
                        //Json::StyledWriter stylewriter;//json格式写入器
                        LOG(INFO, req.body);
                        /*获取本次要读取的top记录*/ 
                       /*ajax前端传入的文本是json格式, 前端利用localStorage保存当前top值,如果后端来做需要保证每个用户的当前top值，并且并发场景下需要加锁维护 */
                        Json::Value v;
                        Json::Reader reader;//读取器
                        Json::Value jsonObj;
                        if (reader.parse(req.body, jsonObj)) {
                            /*点击了记录查询按钮*/
                            std::string type = jsonObj["thing"].asString();
                            /*num标记当前获取数据块的尾指针*/
                            /*asInt有bug*/
                            int num = atoi(jsonObj["num"].asString().c_str());
                            /*倒着来的*/
                            if (type == "prev") {
                                num += 10;
                            }
                            else if(type == "next") {
                                num -= 10;
                            }
                            else if (type == "init") {
                                num = _ojModelObj.GetHistorySize(username);
                            }
                            if (!jsonObj["num"].isNull()) {
                                num = _ojModelObj.HistoryQuery(type, v, num,username);/*更新返回*/
                            }
                            else {
                                LOG(ERROR, "catch unknown jsonObj");
                            }
                            v["top"] = std::to_string(num);
                            LOG(INFO, v.toStyledString());
                            res.status = 200;
                            res.set_content(v.toStyledString(), "application/json");
                        }
                        else {
                            LOG(ERROR, "reader parse error");
                            res.status = 404;
                        }
              });
        }
        /*HistoryStatus记录显示,每次一页最多显示10条记录,采用前端ajax渲染,这里只要返回数据就可以了*/
        bool SetHistoryStatusPage() {
              _server.Post(R"(/statusD)", [&](const httplib::Request& req, httplib::Response& res) {
                        LOG(INFO, req.body);
                        /*获取本次要读取的top记录*/ 
                       /*ajax前端传入的文本是json格式, 前端利用localStorage保存当前top值,如果后端来做需要保证每个用户的当前top值，并且并发场景下需要加锁维护 */
                        Json::Value v;
                        Json::Reader reader;//读取器
                        Json::Value jsonObj;
                        if (reader.parse(req.body, jsonObj)) {
                            /*点击了记录查询按钮*/
                            std::string type = jsonObj["thing"].asString();
                            /*num标记当前获取数据块的尾指针*/
                            /*asInt有bug*/
                            int num = atoi(jsonObj["num"].asString().c_str());
                            /*倒着来的*/
                            if (type == "prev") {
                                num += 10;
                            }
                            else if(type == "next") {
                                num -= 10;
                            }
                            else if (type == "init") {
                                num = _ojModelObj.GetHistorySize();
                            }
                            if (!jsonObj["num"].isNull()) {
                                num = _ojModelObj.HistoryQuery(type, v, num);/*更新返回*/
                            }
                            else {
                                LOG(ERROR, "catch unknown jsonObj");
                            }
                            v["top"] = std::to_string(num);
                            LOG(INFO, v.toStyledString());
                            res.status = 200;
                            res.set_content(v.toStyledString(), "application/json");
                        }
                        else {
                            LOG(ERROR, "reader parse error");
                            res.status = 404;
                        }
              });
        } 
      private:
        /*发送邮箱*/
        bool OjSendEmail(const std::string& EndPointEmail, const std::string& Password) {
              LOG(INFO, "OjSendEmail begin...");
              std::string Message = "账号: " + EndPointEmail + "\n密码: " + Password;
            
            	CSmtp smtp(
            	    	25,								                	/*smtp端口*/
            	    	"smtp.126.com",						          /*smtp服务器地址*/
            	    	"yyycodeoj@126.com",				        /*你的邮箱地址*/
            	    	"AGFKYYGJXFHUSVKQ",					        /*邮箱密码*/
            	    	 EndPointEmail,                     /*目的邮箱地址*/
            	     	"Oj账号和密码",							          /*主题*/
            	    	 Message	                        /*邮件正文*/
            	);
              /*发送邮箱*/
            	int err;
            	if ((err = smtp.SendEmail_Ex()) != 0)
            	{
            		if (err == 1)
                  LOG(ERROR, "错误1: 由于网络不畅通，发送失败!");
            		if (err == 2)
                  LOG(ERROR, "错误2: 用户名错误,请核对!");
            		if (err == 3)
                  LOG(ERROR, "错误3: 用户密码错误，请核对!");
            		if (err == 4)
                  LOG(ERROR, "错误4: 请检查附件目录是否正确，以及文件是否存在!");
                return false;
            	}
              return true;
        }
      private:
        Oj_Control() = default;
      private:
        static Oj_Control* _ojControl;
        static httplib::Server _server;
        static std::mutex _mtx;
        static Oj_Model _ojModelObj;
  };
  Oj_Control* Oj_Control::_ojControl = nullptr;
  httplib::Server Oj_Control::_server; /*默认构造*/
  std::mutex Oj_Control::_mtx;
  Oj_Model Oj_Control:: _ojModelObj;
}
